/*
 * 智能语言控制控制，支持天猫、小爱、小度、google Assistent同时控制
 * 2022-08-22
 * QQ交流群：566565915
 * 官网https://bemfa.com
 */

#define server_ip "bemfa.com" // 巴法云服务器地址默认即可
#define server_port "8344"    // 服务器端口，tcp创客云端口8344

//********************需要修改的部分*******************//

#define wifi_name "Sensen"  // WIFI名称，区分大小写，不要写错
#define wifi_password "15367660" // WIFI密码

String UID = "4892bd7fe005ecbbccf35929157ec7e7"; // 用户私钥，可在控制台获取,修改为自己的UID
String TOPIC = "";                               // 主题名字，可在控制台新建
String aptype = "006";                           // 001插座类型，002灯类型，003风扇类型，004传感器，005空调，006开关，009窗帘

// String TOPIC = "update"; // 主题名字，可在控制台新建
int i = 0;
String k1 = "on";
String k2 = "on";
String k3 = "on";
String k4 = "on";
String k0 = "on";
//**************************************************//
// 最大字节数
#define MAX_PACKETSIZE 512
// 设置心跳值60s
#define KEEPALIVEATIME 2 * 1000
// tcp客户端相关初始化，默认即可
int failCount = 0; // 定义失败连接次数
WiFiClient TCPclient;
String TcpClient_Buff = ""; // 初始化字符串，用于接收服务器发来的数据
unsigned int TcpClient_BuffIndex = 0;
unsigned long TcpClient_preTick = 0;
unsigned long preHeartTick = 0;    // 心跳
unsigned long preTCPStartTick = 0; // 连接
bool preTCPConnected = false;
hw_timer_t *tim = NULL; // 声明看门狗指针变量

void startSTA();
void doWiFiTick();
void doTCPClientTick();
void startTCPClient();
void sendtoTCPServer(String p);
void delayRestart(float t);
void restoreFactory();
void yourFunction();
// bool processFile(String targetValue, char timeData[], char parameterData[]);
void operateOnDataInFile(const char *filename, String targetChar, String action, int &newData);
/**
 * 固件升级函数
 * 在需要升级的地方，加上这个函数即可，例如setup中加的updateBin();
 * 原理：通过http请求获取远程固件，实现升级
 */

// 当升级开始时，打印日志
void update_started()
{
  Serial.println("CALLBACK:  HTTP update process started");
  update = true;
  // digitalWrite(spk_PIN, HIGH); // 指示灯引脚
}

// 当升级结束时，打印日志
void update_finished()
{
  Serial.println("CALLBACK:  HTTP update process finished");
}

// 当升级中，打印日志
void update_progress(int cur, int total)
{
  timerWrite(tim, 0); // 喂狗操作，计时器清零
  static bool update = false;
  int bfb = map(cur, 0, total, 0, 100);
  if (bfb % 5 == 0)
  {
    if (!update)
    {
      sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "&msg=process#" + bfb + "%\r\n"); // 推送消息
      update = true;
    }
  }
  else
  {
    update = false;
  }
}

// 当升级失败时，打印日志
void update_error(int err)
{
  Serial.printf("CALLBACK:  HTTP update fatal error code %d\n", err);
}

// 升级超时中断函数
void tim1Interrupt()
{
  Serial.println("喂狗超时系统重启");
  delay(1000);
  esp_restart();
}
TaskHandle_t TASK_HandleOne = NULL;

// 升级主程序函数
void updateBin()
{
  String upUrl = "http://bin.bemfa.com/b/3BcNDg5MmJkN2ZlMDA1ZWNiYmNjZjM1OTI5MTU3ZWM3ZTc=" + TOPIC + ".bin"; // OTA固件链接，请替换为自己的固件链接，如果接收到msg=update，开始执行固件升级
  Serial.println(ESP.getFreeHeap());
  Serial.println("开始升级");
  WiFiClient UpdateClient;
  httpUpdate.onStart(update_started);     // 当升级开始时
  httpUpdate.onEnd(update_finished);      // 当升级结束时
  httpUpdate.onProgress(update_progress); // 当升级中
  httpUpdate.onError(update_error);       // 当升级失败时

  t_httpUpdate_return ret = httpUpdate.update(UpdateClient, upUrl);
  switch (ret)
  {
  case HTTP_UPDATE_FAILED: // 当升级失败
    Serial.println("当升级失败");
    vTaskDelete(NULL);
    break;
  case HTTP_UPDATE_NO_UPDATES: // 当无升级
    Serial.println("当无升级");
    break;
  case HTTP_UPDATE_OK: // 当升级成功
    Serial.println("当升级成功");
    break;
  }
}

/*
  网络连接
 */
void startSTA()
{
  Serial.println("连接网络......");
  WiFi.disconnect();                    // 断开连接
  WiFi.mode(WIFI_STA);                  // STA模式
  WiFi.begin(wifi_name, wifi_password); // 连接路由器
}

/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上，若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick()
{
  static bool taskStarted = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (WiFi.status() != WL_CONNECTED) // 检查是否连接成功
  {
    digitalWrite(spk_PIN, !digitalRead(spk_PIN)); // 指示灯引脚
    delay(200);
    if (millis() - lastWiFiCheckTick > 30000) // 未连接重连
    {
      Serial.println("网络未连接重连");
      startSTA();
      lastWiFiCheckTick = millis();
    }
    taskStarted = false;
  }
  else if (taskStarted == false)
  {
    digitalWrite(spk_PIN, LOW); // 指示灯引脚
    Serial.println("网络连接成功");
    Serial.print("IP地址:");
    Serial.println(WiFi.localIP());
    taskStarted = true;
    startTCPClient();
  }
}
/*
 *发送数据到TCP服务器
 */
void sendtoTCPServer(String p)
{
  if (!TCPclient.print(p))
  {
    Serial.println("TCPclien发送失败");
    TCPclient.stop();
  }
  timerWrite(tim, 0); // 喂狗操作，计时器清零
  preHeartTick = millis(); // 心跳计时开始，需要每隔60秒发送一次数据
}
/*
 *初始化和服务器建立连接
 */
void startTCPClient()
{
  Serial.println("TCPclien开始连接");
  if (TCPclient.connect(server_ip, atoi(server_port)))
  {
    String mac = WiFi.macAddress().substring(8);               // 取mac地址做主题用
    mac.replace(":", "");                                      // 去掉:号
    TOPIC = mac + aptype;                                      // 主题添加类型
    String tcpTemp = "";                                       // 初始化字符串
    tcpTemp = "cmd=1&uid=" + UID + "&topic=" + TOPIC + "\r\n"; // 构建订阅指令
    Serial.print("发送订阅指令:" + tcpTemp);
    sendtoTCPServer(tcpTemp); // 发送订阅指令
    tcpTemp = "";             // 清空
    /*
     //如果需要订阅多个主题，可发送  cmd=1&uid=xxxxxxxxxxxxxxxxxxxxxxx&topic=xxx1,xxx2,xxx3,xxx4\r\n
    教程：https://bbs.bemfa.com/64
     */
    TCPclient.setNoDelay(true);
    Serial.println("TCPclien连接成功");
  }
  else
  {
    failCount = failCount + 1;
    if (failCount > 10) // 如果失败连接10次
    {
      failCount = 0;
      WiFi.disconnect(); // 断开连接
    }
    Serial.println();
    Serial.println("TCPclien连接失败");
    TCPclient.stop();
  }
  preTCPStartTick = millis();
}
/*
 *接收数据，发送数据
 */
void doTCPClientTick()
{
  i++;
  if (WiFi.status() != WL_CONNECTED)
  {
    preTCPStartTick = millis();
    return;
  }
  if (!TCPclient.connected()) // 断开重连
  {
    if (millis() - preTCPStartTick > 3000) // 重新连接
    {
      startTCPClient();
    }
  }
  else
  {
    if (TCPclient.available())
    {
      // 收数据
      char c = TCPclient.read();
      TcpClient_Buff += c;
      TcpClient_BuffIndex++;
      TcpClient_preTick = millis();

      if (TcpClient_BuffIndex >= MAX_PACKETSIZE - 1)
      {
        TcpClient_BuffIndex = MAX_PACKETSIZE - 2;
        TcpClient_preTick = TcpClient_preTick - 200;
      }
    }
    if (millis() - preHeartTick >= KEEPALIVEATIME)
    {
      // 保持心跳
      // Serial.println("保持心跳");
      // sendtoTCPServer("cmd=0&msg=keep\r\n");
      sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=" + ble_U + "#" + ble_I + "#" + int(ble_dl) + "#" + ble_T1 +
                      "#" + ble_T2 + "#" + ble_U1 + "#" + ble_U2 + "#" + ble_U3 +
                      "#" + ble_U4 + "#" + xdc_U / 1000 + "#" + xdc_I / 1000 + "#" + Q + "#" + MOS_T +
                      "#" + tyn_U / 1000 + "#" + tyn_I / 1000 + "#" + tyn_P + "#" + int(kWh) + "#" + i +
                      "#" + out + "#" + mppt_kg + "#" + ble + "#" + in + "#" + ESP.getMinFreeHeap() + "\r\n"); // 推送消息
      i = 0;
      timerWrite(tim, 0); // 喂狗操作，计时器清零
    }
    if ((millis() - TcpClient_preTick >= 200) && (TcpClient_Buff.length() >= 70))
    {
      TCPclient.flush();
      // Serial.println(TcpClient_Buff.length());
      // Serial.println(TcpClient_Buff);

      if (TcpClient_Buff.indexOf("Resetcharts") > 0) // 清空数据
      {
        Serial.println("重置图表");
        if (getLocalTime(&timeinfo))
        {
          // 清空文件
          File file = SPIFFS.open("/charts.txt", "w");
          if (!file)
          {
            Serial.println("Failed to open file for writing");
            return;
          }
          file.close();
          sendtoTCPServer("cmd=2&uid=" + UID +
                          "&topic=" + TOPIC +
                          "/up&msg=重置成功\r\n");
        }
      }
      else if (TcpClient_Buff.indexOf("delete") > 0)
      {
        File root = SPIFFS.open("/");
        if (!root)
        {
          sendtoTCPServer("cmd=2&uid=" + UID +
                          "&topic=" + TOPIC +
                          "/up&msg=打开失败\r\n");
        }
        int a = TcpClient_Buff.indexOf("delete:");
        int b = TcpClient_Buff.indexOf("\n", a);
        if (a && b)
        {
          String VaLue = TcpClient_Buff.substring(a + 7, b);
          if (SPIFFS.remove("/" + VaLue + ""))
          {

            Serial.println(" remove sucess");
            sendtoTCPServer("cmd=2&uid=" + UID +
                            "&topic=" + TOPIC +
                            "/up&msg=remove sucess\r\n");
          }
        }
        for (size_t i = 0; File file = root.openNextFile(); i++)
        {
          sendtoTCPServer("cmd=2&uid=" + UID +
                          "&topic=" + TOPIC +
                          "/up&msg=" + file.name() + "#" + file.size() + "\r\n"); // 推送消息
          while (file.available())
          {
            String content = "";
            int length = 127;
            content = file.readStringUntil('\n');
            for (size_t i = 0; i < content.length(); i += length)
            {
              sendtoTCPServer("cmd=2&uid=" + UID +
                              "&topic=" + TOPIC +
                              "/up&msg=" + content.substring(i, min(i + length, content.length())) + "\r\n");
            }
            delay(500);
          }
          delay(500);
          file.close();
        }
        // File file = SPIFFS.open("/charts.txt", "r");
        // if (file)
        // {
        //   const size_t bytesToKeep = 217; // 假设要保留前100字节
        //   char buffer[bytesToKeep];
        //   size_t bytesRead = file.read((uint8_t *)buffer, bytesToKeep);
        //   file.close();
        //   // 后续步骤
        //   File file = SPIFFS.open("/charts.txt", "w");
        //   if (file)
        //   {
        //     file.write((uint8_t *)buffer, bytesRead);
        //     file.close();
        //   }
        // }
      }
      else if (TcpClient_Buff.indexOf("charts") > 0) // 把图表数据发送出去
      {
        yourFunction();
        int a = TcpClient_Buff.indexOf("charts") + 6;
        int b = TcpClient_Buff.indexOf("#");
        String VaLue = TcpClient_Buff.substring(a, b);
        Serial.println(VaLue);
        char parameter[125]; // 假设最多存储 100 组参数数据
        if (processFile(VaLue, parameter))
        {
          Serial.printf("找到数据%s", parameter);
          sendtoTCPServer("cmd=2&uid=" + UID +
                          "&topic=" + TOPIC +
                          "/up&msg=M" + parameter + "\r\n"); // 推送消息
        }
        else
        {
          Serial.println("没有找到数据");
          sendtoTCPServer("cmd=2&uid=" + UID +
                          "&topic=" + TOPIC +
                          "/up&msg=没有找到数据\r\n"); // 推送消息
        }
      }
      else if (TcpClient_Buff.indexOf("001#on") > 0) // 如果是消息==打开
      {
        Serial.println("充电开");
        out = true;
        in = false;
        preHeartTick = 0; // 心跳计时清零，发送一次数据
      }
      else if (TcpClient_Buff.indexOf("001#off") > 0)
      {
        Serial.println("充电关");
        out = false;
        preHeartTick = 0; // 心跳计时清零，发送一次数据
      }
      else if (TcpClient_Buff.indexOf("002#on") > 0)
      {
        Serial.println("智能充电开");
        mppt_kg = true;
        preHeartTick = 0; // 心跳计时清零，发送一次数据
      }
      else if (TcpClient_Buff.indexOf("002#off") > 0)
      {
        Serial.println("智能充电关");
        mppt_kg = false;
        Utg = 13000;
        preHeartTick = 0; // 心跳计时清零，发送一次数据
      }
      else if (TcpClient_Buff.indexOf("ble#off") > 0)
      {
        ble = false;
      }
      else if (TcpClient_Buff.indexOf("ble#on") > 0)
      {
        ble = true;
      }
      else if (TcpClient_Buff.indexOf("Reboot") > 0)
      {
        Serial.println("重启");
        delayRestart(1);
      }
      else if (TcpClient_Buff.indexOf("Reset") > 0) // 清空数据
      {
        Serial.println("重置电量");
        File file = SPIFFS.open("/data.txt", "w"); // 创建新文件
        Q = 0;
        // kWh = 0;
        // zuoTiankWh = 0;
        operateOnDataInFile("/data.txt", "Q", "W", Q);
        // operateOnDataInFile("/data.txt", "kWh", "W", kWh);
        // operateOnDataInFile("/data.txt", "zuoTiankWh", "W", zuoTiankWh);
      }
      else if (TcpClient_Buff.indexOf("update") > 0)
      {
        Serial.println("升级");
        updateBin();
      }
      if (TcpClient_Buff.indexOf("shezhi") > 0)
      {
        int a = 0;
        int b = 0;
        int num = 0;
        String c = "";
        if (TcpClient_Buff.indexOf("Uxm:") > 0)
        {
          a = TcpClient_Buff.indexOf("Uxm:") + 4;  // c语言字符串查找，查找&msg=位置
          b = TcpClient_Buff.indexOf("\n", a) - 2; // c语言字符串查找，查找&msg=位置
          c = TcpClient_Buff.substring(a, b);      // c语言字符串截取，截取到topic,不懂的可百度c语言字符串截取
          num = atoi(c.c_str());
          if (num >= 13000 && num <= 18000)
          {
            Uxm = num;
            Ux = num;
            config.Uxm = num; // 设置蓄电池满电电压
          }
        }

        else if (TcpClient_Buff.indexOf("Uxf:") > 0)
        {
          a = TcpClient_Buff.indexOf("Uxf:") + 4;  // c语言字符串查找，查找&msg=位置
          b = TcpClient_Buff.indexOf("\n", a) - 2; // c语言字符串查找，查找&msg=位置
          c = TcpClient_Buff.substring(a, b);      // c语言字符串截取，截取到topic,不懂的可百度c语言字符串截取
          num = atoi(c.c_str());
          if (num >= 13800 && num <= 14200)
          {
            Uxf = num;
            config.Uxf = num; // 设置蓄电池浮充电压
          }
        }

        else if (TcpClient_Buff.indexOf("It:") > 0)
        {
          a = TcpClient_Buff.indexOf("It:") + 3;   // c语言字符串查找，查找&msg=位置
          b = TcpClient_Buff.indexOf("\n", a) - 2; // c语言字符串查找，查找&msg=位置
          c = TcpClient_Buff.substring(a, b);      // c语言字符串截取，截取到topic,不懂的可百度c语言字符串截取
          num = atoi(c.c_str());
          if (num >= 1000 && num <= 80000)
          {
            It = num;
            config.It = num; // 设置充电电流
          }
        }

        else if (TcpClient_Buff.indexOf("Utg:") > 0)
        {
          a = TcpClient_Buff.indexOf("Utg:") + 4;  // c语言字符串查找，查找&msg=位置
          b = TcpClient_Buff.indexOf("\n", a) - 2; // c语言字符串查找，查找&msg=位置
          c = TcpClient_Buff.substring(a, b);      // c语言字符串截取，截取到topic,不懂的可百度c语言字符串截取
          num = atoi(c.c_str());
          if (num >= 13000 && num <= 18000)
          {
            Utg = num;
          }
        }
        else if (TcpClient_Buff.indexOf("T:") > 0)
        {
          a = TcpClient_Buff.indexOf("T:") + 2;    // c语言字符串查找，查找&msg=位置
          b = TcpClient_Buff.indexOf("\n", a) - 2; // c语言字符串查找，查找&msg=位置
          c = TcpClient_Buff.substring(a, b);      // c语言字符串截取，截取到topic,不懂的可百度c语言字符串截取
          num = atoi(c.c_str());
          if (num >= 30 && num <= 60)
          {
            T = num;
            config.T = num; // 设置温度保护
          }
        }
        saveConfig();
        sendtoTCPServer(
            "cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=canshu#" +
            Uxm / 1000 + "#" + It / 1000 + "#" + Uxf / 1000 + "#" +
            Utg / 1000 + "#" + T + "#\r\n"); // 推送消息
      }
      TcpClient_Buff = "";
      TcpClient_BuffIndex = 0;
    }
  }
}

void kanmenggou()
{
  // 初始化看门狗定时器
  tim = timerBegin(1, 80, true);
  timerAttachInterrupt(tim, tim1Interrupt, true);
  timerAlarmWrite(tim, 60000 * 1000, true); // 30s触发
  timerAlarmEnable(tim);
}

/*
 *启动巴法云
 */
void qidongBFY()
{
  doWiFiTick();
  doTCPClientTick();
}
